Background

Rob Baker et al fit growth parameters to B. rapa growth data and then used those to do function-value-trait QTL mapping.

I used those same parameters and queried a mutual rank (MR) gene expression network to find genes that were in a networks associated with these growth paramters. This was done separately for the CR and UN environments. I looked for overlap between those MR-associated genes and growth parameters.

The goal now is to find the trans eQTL for each of the MR-growth associated genes and ask if the trans eQTL overlap with the growth/function-value QTL. I think I am only going to take the top eQTL for each.

Focused on UN genes only.

This is for the CIM eQTL results

Methodology

For each gene in a MR network with a growth parameter query the eQTL database to find its trans eQTL regions. Then compare overlaps between those and the growth QTL.

Load the libraries and data

Libraries

library(qtl)
library(GenomicRanges)
Loading required package: stats4
Loading required package: BiocGenerics
Loading required package: parallel

Attaching package: ‘BiocGenerics’

The following objects are masked from ‘package:parallel’:

    clusterApply, clusterApplyLB, clusterCall, clusterEvalQ, clusterExport,
    clusterMap, parApply, parCapply, parLapply, parLapplyLB, parRapply,
    parSapply, parSapplyLB

The following objects are masked from ‘package:stats’:

    IQR, mad, sd, var, xtabs

The following objects are masked from ‘package:base’:

    anyDuplicated, append, as.data.frame, basename, cbind, colMeans,
    colnames, colSums, dirname, do.call, duplicated, eval, evalq, Filter,
    Find, get, grep, grepl, intersect, is.unsorted, lapply, lengths, Map,
    mapply, match, mget, order, paste, pmax, pmax.int, pmin, pmin.int,
    Position, rank, rbind, Reduce, rowMeans, rownames, rowSums, sapply,
    setdiff, sort, table, tapply, union, unique, unsplit, which, which.max,
    which.min

Loading required package: S4Vectors

Attaching package: ‘S4Vectors’

The following object is masked from ‘package:base’:

    expand.grid

Loading required package: IRanges
Loading required package: GenomeInfoDb
library(tidyverse)
── Attaching packages ───────────────────────────────────────────── tidyverse 1.2.1 ──
✔ ggplot2 3.0.0     ✔ purrr   0.2.5
✔ tibble  1.4.2     ✔ dplyr   0.7.6
✔ tidyr   0.8.1     ✔ stringr 1.3.1
✔ readr   1.1.1     ✔ forcats 0.3.0
── Conflicts ──────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::collapse()   masks IRanges::collapse()
✖ dplyr::combine()    masks BiocGenerics::combine()
✖ dplyr::desc()       masks IRanges::desc()
✖ tidyr::expand()     masks S4Vectors::expand()
✖ dplyr::filter()     masks stats::filter()
✖ dplyr::first()      masks S4Vectors::first()
✖ dplyr::lag()        masks stats::lag()
✖ ggplot2::Position() masks BiocGenerics::Position(), base::Position()
✖ purrr::reduce()     masks GenomicRanges::reduce(), IRanges::reduce()
✖ dplyr::rename()     masks S4Vectors::rename()
✖ dplyr::slice()      masks IRanges::slice()
library(magrittr)

Attaching package: ‘magrittr’

The following object is masked from ‘package:purrr’:

    set_names

The following object is masked from ‘package:tidyr’:

    extract

Growth QTL

filepath <- "../input/All2012HeightQTL2.xlsx"
filebase <- filepath %>% basename() %>% str_replace("\\..*$","")
QTLgenes <- readxl::read_excel(filepath)[,-1]
QTLgenes <- QTLgenes %>% dplyr::rename(.id=QTL, FVTtrait=FVT) # change names to match previous file
QTLgenes <- QTLgenes %>% filter(str_detect(FVTtrait,"^UN"))
QTLgenes

MR genes from UN

MR_UN_genes <- read_csv("../output/MR_UN_graphs_node_annotation_2012.csv") %>%
  filter(MR_Cutoff <= 50, !duplicated(name)) %>%
  mutate(pos=floor((start+end)/2)) %>%
  select(MR_Cutoff,name, transcript_chrom=chrom, transcript_pos=pos)
Parsed with column specification:
cols(
  .default = col_integer(),
  .id = col_character(),
  trt = col_character(),
  name = col_character(),
  chrom = col_character(),
  subject = col_character(),
  AGI = col_character(),
  At_symbol = col_character(),
  At_description = col_character(),
  perc_ID = col_double(),
  eval = col_double(),
  score = col_double()
)
See spec(...) for full column specifications.
MR_UN_genes

eQTL

load("../output/scanone-MRgene-qtl_2012.RData")
scanone_CIM_UN <- scanone_MR_cim
scanone_CIM_UN <- scanone_CIM_UN[!str_detect(rownames(scanone_CIM_UN),"loc"),] #downstream code cannot deal with interpolated markers.

Find eQTL intervals

UN data

Get the scanone data in a nice format for summarizing and plotting

scanone_UN_gather <- scanone_CIM_UN %>%
  gather(key = gene, value = LOD, -chr, -pos) %>%
  right_join(MR_UN_genes,by=c("gene"="name")) # only keep genes in MR networks

plot eQTL peaks…

pl.UN <- scanone_UN_gather %>%
  ggplot(aes(x=pos,y=LOD,color=gene)) +
  geom_line() +
  geom_hline(aes(yintercept=mean(lod.thrs.cim)),lty=2,lwd=.5,alpha=.5) +
  facet_grid( ~ chr, scales="free") +
  theme(strip.text.y = element_text(angle=0), axis.text.x = element_text(angle=90)) +
  ggtitle("MR gene eQTL")
pl.UN

ggsave(str_c("../output/MR50 gene eQTL UN CIM", Sys.Date(), ".pdf"),width=12,height=8)
ggsave(str_c("../output/MR50 gene eQTL UN CIM", Sys.Date(), ".png"),width=10,height=5)
pl.UN + coord_cartesian(ylim=c(0,10))

plot eQTL peaks for MR30

pl.UN <- scanone_UN_gather %>%
  filter(MR_Cutoff <=30) %>%
  ggplot(aes(x=pos,y=LOD,color=gene)) +
  geom_line() +
  geom_hline(aes(yintercept=mean(lod.thrs.cim)),lty=2,lwd=.5,alpha=.5) +
  facet_grid( ~ chr, scales="free") +
  theme(strip.text.y = element_text(angle=0), axis.text.x = element_text(angle=90)) +
  ggtitle("MR gene eQTL")
pl.UN

ggsave(str_c("../output/MR30 gene eQTL UN CIM", Sys.Date(), ".pdf"),width=12,height=8)
ggsave(str_c("../output/MR30 gene eQTL UN CIM", Sys.Date(), ".png"),width=10,height=5)

cis and trans plot

scanone_UN_gather %>%
  arrange(transcript_chrom,transcript_pos,pos) %>%
  filter(LOD>mean(lod.thrs.cim)) %>%
  group_by(gene,chr) %>%
  filter(LOD==max(LOD)) %>%
  ungroup() %>%
  mutate(transcript_index=row_number(),cis_trans=ifelse(chr==transcript_chrom,"cis","trans")) %>%
  ggplot(aes(x=pos,y=transcript_index,shape=cis_trans,color=LOD)) +
  scale_color_gradient(low="magenta1",high="magenta4") +
  geom_point() +
  facet_wrap(~chr,nrow=1) +
  theme_bw() + 
  xlab("QTL position")
ggsave(str_c("../output/MR_gene_eQTL_cistrans_UN_CIM_", Sys.Date(), ".png"),width=10,height = 5)

sig_chromosomes_UN <- scanone_UN_gather %>%
  
  group_by(gene,chr) %>%
  summarize(pos=pos[which.max(LOD)],LOD=max(LOD)) %>%
  filter(LOD > mean(lod.thrs.cim))
sig_chromosomes_UN

now for each significant chromosome/trait combo run bayesint

bayesint_list_UN <- apply(sig_chromosomes_UN,1,function(hit) {
  result <- bayesint(scanone_CIM_UN[c("chr","pos",hit["gene"])], 
                     chr=hit["chr"], 
                     lodcolumn = 1,
                     prob=0.99,
                     expandtomarkers = TRUE
  )
  colnames(result)[3] <- "LOD"
  result
})
names(bayesint_list_UN) <- sig_chromosomes_UN$gene
bayesint_list_UN <- lapply(bayesint_list_UN,function(x) x %>% 
                             as.data.frame(stringsAsFactors=FALSE) %>%
                             rownames_to_column(var="markername") %>%
                             mutate(chr=as.character(chr))
)
bayesint_result_UN <- as.tibble(bind_rows(bayesint_list_UN,.id="gene")) %>% 
  select(gene,chr,pos,markername,LOD) %>%
  separate(markername,into=c("chr1","Mbp"),sep="x", convert=TRUE) %>%
  group_by(gene,chr) %>%
  summarize(eQTL_start=min(Mbp),eQTL_end=max(Mbp),min_eQTL_LOD=min(LOD),max_eQTL_LOD=max(LOD)) %>%
  #for the high QTL peaks the interval width is 0.  That is overly precise and need to widen those.
  mutate(eQTL_start=ifelse(eQTL_start==eQTL_end,max(0,eQTL_start-20000),eQTL_start),
         eQTL_end=ifelse(eQTL_start==eQTL_end,eQTL_end+20000,eQTL_end))
bayesint_result_UN

annotate UN eQTL

Load annotation

BrapaAnnotation <- read_csv("../input/Brapa_V1.5_annotated.csv")
Missing column names filled in: 'X1' [1]Parsed with column specification:
cols(
  X1 = col_integer(),
  name = col_character(),
  chrom = col_character(),
  start = col_integer(),
  end = col_integer(),
  subject = col_character(),
  AGI = col_character(),
  At_symbol = col_character(),
  At_description = col_character(),
  perc_ID = col_double(),
  aln_length = col_integer(),
  mismatch = col_integer(),
  gap_open = col_integer(),
  qstart = col_integer(),
  qend = col_integer(),
  sstart = col_integer(),
  send = col_integer(),
  eval = col_double(),
  score = col_double()
)

|====                                                                          |   5%
|=====                                                                         |   7%
|======                                                                        |   8%
|=======                                                                       |   9%
|========                                                                      |  10%
|=========                                                                     |  11%
|==========                                                                    |  13%
|==========                                                            |  14%    1 MB
|===========                                                           |  15%    1 MB
|===========                                                           |  16%    1 MB
|============                                                          |  17%    1 MB
|=============                                                         |  19%    1 MB
|==============                                                        |  20%    1 MB
|===============                                                       |  21%    1 MB
|================                                                      |  22%    1 MB
|=================                                                     |  24%    1 MB
|==================                                                    |  25%    1 MB
|==================                                                    |  26%    1 MB
|===================                                                   |  27%    2 MB
|====================                                                  |  29%    2 MB
|=====================                                                 |  30%    2 MB
|======================                                                |  31%    2 MB
|=======================                                               |  32%    2 MB
|========================                                              |  34%    2 MB
|=========================                                             |  35%    2 MB
|=========================                                             |  36%    2 MB
|==========================                                            |  37%    2 MB
|===========================                                           |  38%    2 MB
|============================                                          |  40%    2 MB
|=============================                                         |  41%    3 MB
|==============================                                        |  42%    3 MB
|===============================                                       |  43%    3 MB
|===============================                                       |  44%    3 MB
|================================                                      |  46%    3 MB
|=================================                                     |  47%    3 MB
|==================================                                    |  48%    3 MB
|===================================                                   |  49%    3 MB
|====================================                                  |  51%    3 MB
|=====================================                                 |  52%    3 MB
|=====================================                                 |  53%    3 MB
|======================================                                |  54%    4 MB
|=======================================                               |  55%    4 MB
|========================================                              |  57%    4 MB
|=========================================                             |  58%    4 MB
|==========================================                            |  59%    4 MB
|===========================================                           |  60%    4 MB
|===========================================                           |  61%    4 MB
|============================================                          |  62%    4 MB
|=============================================                         |  64%    4 MB
|==============================================                        |  65%    4 MB
|===============================================                       |  66%    4 MB
|================================================                      |  67%    5 MB
|=================================================                     |  69%    5 MB
|=================================================                     |  70%    5 MB
|==================================================                    |  71%    5 MB
|===================================================                   |  72%    5 MB
|====================================================                  |  73%    5 MB
|=====================================================                 |  75%    5 MB
|======================================================                |  76%    5 MB
|=======================================================               |  77%    5 MB
|========================================================              |  78%    5 MB
|========================================================              |  80%    5 MB
|=========================================================             |  81%    6 MB
|==========================================================            |  82%    6 MB
|===========================================================           |  83%    6 MB
|============================================================          |  84%    6 MB
|=============================================================         |  86%    6 MB
|=============================================================         |  87%    6 MB
|==============================================================        |  88%    6 MB
|===============================================================       |  89%    6 MB
|================================================================      |  90%    6 MB
|=================================================================     |  92%    6 MB
|==================================================================    |  93%    6 MB
|===================================================================   |  94%    7 MB
|====================================================================  |  95%    7 MB
|====================================================================  |  97%    7 MB
|===================================================================== |  98%    7 MB
|======================================================================|  99%    7 MB
|=======================================================================| 100%    7 MB
BrapaAnnotation
UN_annotated <- lapply(1:nrow(bayesint_result_UN),function(row) {
  qtl <- bayesint_result_UN[row,]
  subset(BrapaAnnotation, chrom==qtl$chr &
           start >= qtl$eQTL_start &
           end <= qtl$eQTL_end) 
}
)
names(UN_annotated) <- bayesint_result_UN$gene
UN_annotated <- bind_rows(UN_annotated,.id="MR_gene") %>%
  left_join(bayesint_result_UN,by=c("MR_gene"="gene","chrom"="chr")) %>% #get eQTL LOD
  left_join(MR_UN_genes,by=c("MR_gene"="name")) %>% # get cutoff
  dplyr::rename(eQTL_candidate=name)
UN_annotated_small <- UN_annotated %>% select(MR_gene,MR_Cutoff,eQTL_chrom=chrom, eQTL_start, eQTL_end, eQTL_candidate,ends_with("LOD"))
UN_annotated_small
write_csv(UN_annotated_small, path=str_c("../output/", 
                                         filebase, 
                                         "_MR_eQTL_UN_ALL_CIM_", 
                                         Sys.Date(), ".csv"))

cis eQTL?

rr UN_annotated_small %>% filter(MR_gene==eQTL_candidate)

given bayesint results, find overlaps with UN growth QTL

rr UN_MReQTL_QTL_combined <- inner_join(QTLgenes,UN_annotated_small,by=c(=_candidate)) %>% select(.id, MR_gene, MR_Cutoff, eQTL_start, eQTL_end, ends_with(), everything()) %>% arrange(.id,desc(max_eQTL_LOD)) %>% dplyr::rename(eQTL_candidate=name) UN_MReQTL_QTL_combined r UN_MReQTL_QTL_combined_small <- UN_MReQTL_QTL_combined %>% filter(!duplicated(eQTL_candidate)) %>% select(-MR_gene,-MR_Cutoff) UN_MReQTL_QTL_combined_small

cis eQTL?

rr UN_MReQTL_QTL_combined %>% filter(MR_gene==eQTL_candidate) %>% arrange(MR_gene)

Total number of MR genes with an eQTL that overlaps with an FVT

rr UN_MReQTL_QTL_combined %>% select(MR_gene) %>% unique()

how many QTL have at least some overlap?

rr length(unique(QTLgenes$.id))

[1] 16

rr length(unique(UN_MReQTL_QTL_combined$.id))

[1] 10

10 of 16

rr write_csv(UN_MReQTL_QTL_combined, path=str_c(../output/, filebase, _MR_eQTL_UN_overlap_CIM_, Sys.Date(), .csv))

How to assess if overlap is significant?

I think pull regions of same size as eQTL and ask how often they overlap with growth QTL.

For each eQTL, randomly select a chromosome, then a position, and widen based on interval. Then check overlap. Repeat.

Make table of chromosome info

rr chr.info <- scanone_CIM_UN %>% as.data.frame() %>% rownames_to_column() %>% select(marker) %>% separate(marker,into=c(,),sep=,convert=TRUE) %>% group_by(chr) %>% summarize(start=min(bp),end=max(bp))

Make a table of QTL info

rr qtl.info <- QTLgenes %>% group_by(.id) %>% summarize(chrom=unique(chrom),start=min(start),end=max(end)) qtl.info r qtl.ranges <- GRanges(seqnames = qtl.info\(chrom,ranges=IRanges(start=qtl.info\)start,end=qtl.info$end)) qtl.ranges <- GenomicRanges::reduce(qtl.ranges)

The eQTL are in Bayesint_results

rr sims <- 1000 eQTL.ranges <- GRanges(bayesint_result_UN\(chr, ranges = IRanges(start=bayesint_result_UN\)eQTL_start, end=bayesint_result_UN\(eQTL_end)) eQTL.ranges <- GenomicRanges::reduce(eQTL.ranges) set.seed(54321) sim.results <- sapply(1:sims, function(s) { sim.eQTL <- tibble( chr=sample(chr.info\)chr, size = length(eQTL.ranges), replace = TRUE, prob=chr.info\(end/sum(chr.info\)end)), width=width(eQTL.ranges) # width of the QTL to simulate ) sim.eQTL <- chr.info %>% select(chr,chr.start=start,chr.end=end) %>% right_join(sim.eQTL,by=) #need to get the chrom end so we can sample correctly sim.eQTL <- sim.eQTL %>% mutate(qtl.start = runif(n=n(), min = chr.start, max= max(chr.start,chr.end-width)), qtl.end=qtl.start+width) sim.eQTL.ranges <- GRanges(seqnames = sim.eQTL\(chr,ranges = IRanges(start=sim.eQTL\)qtl.start,end=sim.eQTL$qtl.end)) suppressWarnings(result <- sum(countOverlaps(qtl.ranges,sim.eQTL.ranges)>0)) result })

rr true.overlap <- sum(countOverlaps(qtl.ranges,eQTL.ranges)) #OK to ignore warnings

Each of the 2 combined objects has sequence levels not in the other:
  - in 'x': A05
  - in 'y': A04, A01
  Make sure to always combine/compare objects based on the same reference
  genome (use suppressWarnings() to suppress this warning).

rr true.overlap

[1] 6

rr mean(sim.results >= true.overlap)

[1] 0.003

rr tibble(FVTQTL_vs_MReQTL_True_Overlaps=true.overlap, N_Simulations_fewer_overlaps=sum(sim.results < true.overlap), N_Simulations_greater_equal_overlaps=sum(sim.results >= true.overlap), P_value=mean(sim.results >= true.overlap) ) %>% write_csv(str_c(../output/, filebase, _MReQTL_overlap_pval_CIM, Sys.Date(), .csv))

significant at p = 0.003; only 0.3% of the simulations had as many overlaps as in the true data set.

LS0tCnRpdGxlOiAiTXV0dWFsIFJhbmsgZVFUTCBvdmVybGFwIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIyBCYWNrZ3JvdW5kCgpSb2IgQmFrZXIgZXQgYWwgZml0IGdyb3d0aCBwYXJhbWV0ZXJzIHRvIEIuIHJhcGEgZ3Jvd3RoIGRhdGEgYW5kIHRoZW4gdXNlZCB0aG9zZSB0byBkbyBmdW5jdGlvbi12YWx1ZS10cmFpdCBRVEwgbWFwcGluZy4KCkkgdXNlZCB0aG9zZSBzYW1lIHBhcmFtZXRlcnMgYW5kIHF1ZXJpZWQgYSBtdXR1YWwgcmFuayAoTVIpIGdlbmUgZXhwcmVzc2lvbiBuZXR3b3JrIHRvIGZpbmQgZ2VuZXMgdGhhdCB3ZXJlIGluIGEgbmV0d29ya3MgYXNzb2NpYXRlZCB3aXRoIHRoZXNlIGdyb3d0aCBwYXJhbXRlcnMuICBUaGlzIHdhcyBkb25lIHNlcGFyYXRlbHkgZm9yIHRoZSBDUiBhbmQgVU4gZW52aXJvbm1lbnRzLiAgSSBsb29rZWQgZm9yIG92ZXJsYXAgYmV0d2VlbiB0aG9zZSBNUi1hc3NvY2lhdGVkIGdlbmVzIGFuZCBncm93dGggcGFyYW1ldGVycy4KClRoZSBnb2FsIG5vdyBpcyB0byBmaW5kIHRoZSB0cmFucyBlUVRMIGZvciBlYWNoIG9mIHRoZSBNUi1ncm93dGggYXNzb2NpYXRlZCBnZW5lcyBhbmQgYXNrIGlmIHRoZSB0cmFucyBlUVRMIG92ZXJsYXAgd2l0aCB0aGUgZ3Jvd3RoL2Z1bmN0aW9uLXZhbHVlIFFUTC4gIEkgdGhpbmsgSSBhbSBvbmx5IGdvaW5nIHRvIHRha2UgdGhlIHRvcCBlUVRMIGZvciBlYWNoLgoKRm9jdXNlZCBvbiBVTiBnZW5lcyBvbmx5LgoKX19UaGlzIGlzIGZvciB0aGUgQ0lNIGVRVEwgcmVzdWx0c19fCgojIyBNZXRob2RvbG9neQoKRm9yIGVhY2ggZ2VuZSBpbiBhIE1SIG5ldHdvcmsgd2l0aCBhIGdyb3d0aCBwYXJhbWV0ZXIgcXVlcnkgdGhlIGVRVEwgZGF0YWJhc2UgdG8gZmluZCBpdHMgdHJhbnMgZVFUTCByZWdpb25zLiAgVGhlbiBjb21wYXJlIG92ZXJsYXBzIGJldHdlZW4gdGhvc2UgYW5kIHRoZSBncm93dGggUVRMLgoKIyMgTG9hZCB0aGUgbGlicmFyaWVzIGFuZCBkYXRhCgpMaWJyYXJpZXMKYGBge3J9CmxpYnJhcnkocXRsKQpsaWJyYXJ5KEdlbm9taWNSYW5nZXMpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KG1hZ3JpdHRyKQpgYGAKCkdyb3d0aCBRVEwKCmBgYHtyfQpmaWxlcGF0aCA8LSAiLi4vaW5wdXQvQWxsMjAxMkhlaWdodFFUTDIueGxzeCIKZmlsZWJhc2UgPC0gZmlsZXBhdGggJT4lIGJhc2VuYW1lKCkgJT4lIHN0cl9yZXBsYWNlKCJcXC4uKiQiLCIiKQoKUVRMZ2VuZXMgPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKGZpbGVwYXRoKVssLTFdClFUTGdlbmVzIDwtIFFUTGdlbmVzICU+JSBkcGx5cjo6cmVuYW1lKC5pZD1RVEwsIEZWVHRyYWl0PUZWVCkgIyBjaGFuZ2UgbmFtZXMgdG8gbWF0Y2ggcHJldmlvdXMgZmlsZQpRVExnZW5lcyA8LSBRVExnZW5lcyAlPiUgZmlsdGVyKHN0cl9kZXRlY3QoRlZUdHJhaXQsIl5VTiIpKQpRVExnZW5lcwpgYGAKCk1SIGdlbmVzIGZyb20gVU4KYGBge3J9Ck1SX1VOX2dlbmVzIDwtIHJlYWRfY3N2KCIuLi9vdXRwdXQvTVJfVU5fZ3JhcGhzX25vZGVfYW5ub3RhdGlvbl8yMDEyLmNzdiIpICU+JQogIGZpbHRlcihNUl9DdXRvZmYgPD0gNTAsICFkdXBsaWNhdGVkKG5hbWUpKSAlPiUKICBtdXRhdGUocG9zPWZsb29yKChzdGFydCtlbmQpLzIpKSAlPiUKICBzZWxlY3QoTVJfQ3V0b2ZmLG5hbWUsIHRyYW5zY3JpcHRfY2hyb209Y2hyb20sIHRyYW5zY3JpcHRfcG9zPXBvcykKTVJfVU5fZ2VuZXMKYGBgCgplUVRMCmBgYHtyfQpsb2FkKCIuLi9vdXRwdXQvc2Nhbm9uZS1NUmdlbmUtcXRsXzIwMTIuUkRhdGEiKQpzY2Fub25lX0NJTV9VTiA8LSBzY2Fub25lX01SX2NpbQpzY2Fub25lX0NJTV9VTiA8LSBzY2Fub25lX0NJTV9VTlshc3RyX2RldGVjdChyb3duYW1lcyhzY2Fub25lX0NJTV9VTiksImxvYyIpLF0gI2Rvd25zdHJlYW0gY29kZSBjYW5ub3QgZGVhbCB3aXRoIGludGVycG9sYXRlZCBtYXJrZXJzLgpgYGAKCiMjIEZpbmQgZVFUTCBpbnRlcnZhbHMKCgojIyMgVU4gZGF0YQoKR2V0IHRoZSBzY2Fub25lIGRhdGEgaW4gYSBuaWNlIGZvcm1hdCBmb3Igc3VtbWFyaXppbmcgYW5kIHBsb3R0aW5nCmBgYHtyfQpzY2Fub25lX1VOX2dhdGhlciA8LSBzY2Fub25lX0NJTV9VTiAlPiUKICBnYXRoZXIoa2V5ID0gZ2VuZSwgdmFsdWUgPSBMT0QsIC1jaHIsIC1wb3MpICU+JQogIHJpZ2h0X2pvaW4oTVJfVU5fZ2VuZXMsYnk9YygiZ2VuZSI9Im5hbWUiKSkgIyBvbmx5IGtlZXAgZ2VuZXMgaW4gTVIgbmV0d29ya3MKYGBgCgpwbG90IGVRVEwgcGVha3MuLi4KYGBge3IsIGZpZy5oZWlnaHQ9MTB9CnBsLlVOIDwtIHNjYW5vbmVfVU5fZ2F0aGVyICU+JQogIGdncGxvdChhZXMoeD1wb3MseT1MT0QsY29sb3I9Z2VuZSkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdD1tZWFuKGxvZC50aHJzLmNpbSkpLGx0eT0yLGx3ZD0uNSxhbHBoYT0uNSkgKwogIGZhY2V0X2dyaWQoIH4gY2hyLCBzY2FsZXM9ImZyZWUiKSArCiAgdGhlbWUoc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTApLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCkpICsKICBnZ3RpdGxlKCJNUiBnZW5lIGVRVEwiKQpwbC5VTgpnZ3NhdmUoc3RyX2MoIi4uL291dHB1dC9NUjUwIGdlbmUgZVFUTCBVTiBDSU0iLCBTeXMuRGF0ZSgpLCAiLnBkZiIpLHdpZHRoPTEyLGhlaWdodD04KQpnZ3NhdmUoc3RyX2MoIi4uL291dHB1dC9NUjUwIGdlbmUgZVFUTCBVTiBDSU0iLCBTeXMuRGF0ZSgpLCAiLnBuZyIpLHdpZHRoPTEwLGhlaWdodD01KQpwbC5VTiArIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoMCwxMCkpCmBgYAoKcGxvdCBlUVRMIHBlYWtzIGZvciBNUjMwCmBgYHtyLCBmaWcuaGVpZ2h0PTEwfQpwbC5VTiA8LSBzY2Fub25lX1VOX2dhdGhlciAlPiUKICBmaWx0ZXIoTVJfQ3V0b2ZmIDw9MzApICU+JQogIGdncGxvdChhZXMoeD1wb3MseT1MT0QsY29sb3I9Z2VuZSkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdD1tZWFuKGxvZC50aHJzLmNpbSkpLGx0eT0yLGx3ZD0uNSxhbHBoYT0uNSkgKwogIGZhY2V0X2dyaWQoIH4gY2hyLCBzY2FsZXM9ImZyZWUiKSArCiAgdGhlbWUoc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTApLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCkpICsKICBnZ3RpdGxlKCJNUiBnZW5lIGVRVEwiKQpwbC5VTgpnZ3NhdmUoc3RyX2MoIi4uL291dHB1dC9NUjMwIGdlbmUgZVFUTCBVTiBDSU0iLCBTeXMuRGF0ZSgpLCAiLnBkZiIpLHdpZHRoPTEyLGhlaWdodD04KQpnZ3NhdmUoc3RyX2MoIi4uL291dHB1dC9NUjMwIGdlbmUgZVFUTCBVTiBDSU0iLCBTeXMuRGF0ZSgpLCAiLnBuZyIpLHdpZHRoPTEwLGhlaWdodD01KQoKYGBgCgojIyBjaXMgYW5kIHRyYW5zIHBsb3QKCmBgYHtyfQpzY2Fub25lX1VOX2dhdGhlciAlPiUKICBhcnJhbmdlKHRyYW5zY3JpcHRfY2hyb20sdHJhbnNjcmlwdF9wb3MscG9zKSAlPiUKICBmaWx0ZXIoTE9EPm1lYW4obG9kLnRocnMuY2ltKSkgJT4lCiAgZ3JvdXBfYnkoZ2VuZSxjaHIpICU+JQogIGZpbHRlcihMT0Q9PW1heChMT0QpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKHRyYW5zY3JpcHRfaW5kZXg9cm93X251bWJlcigpLGNpc190cmFucz1pZmVsc2UoY2hyPT10cmFuc2NyaXB0X2Nocm9tLCJjaXMiLCJ0cmFucyIpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9cG9zLHk9dHJhbnNjcmlwdF9pbmRleCxzaGFwZT1jaXNfdHJhbnMsY29sb3I9TE9EKSkgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdz0ibWFnZW50YTEiLGhpZ2g9Im1hZ2VudGE0IikgKwogIGdlb21fcG9pbnQoKSArCiAgZmFjZXRfd3JhcCh+Y2hyLG5yb3c9MSkgKwogIHRoZW1lX2J3KCkgKyAKICB4bGFiKCJRVEwgcG9zaXRpb24iKQpnZ3NhdmUoc3RyX2MoIi4uL291dHB1dC9NUl9nZW5lX2VRVExfY2lzdHJhbnNfVU5fQ0lNXyIsIFN5cy5EYXRlKCksICIucG5nIiksd2lkdGg9MTAsaGVpZ2h0ID0gNSkKYGBgCgoKCgoKYGBge3J9CnNpZ19jaHJvbW9zb21lc19VTiA8LSBzY2Fub25lX1VOX2dhdGhlciAlPiUKICAKICBncm91cF9ieShnZW5lLGNocikgJT4lCiAgc3VtbWFyaXplKHBvcz1wb3Nbd2hpY2gubWF4KExPRCldLExPRD1tYXgoTE9EKSkgJT4lCiAgZmlsdGVyKExPRCA+IG1lYW4obG9kLnRocnMuY2ltKSkKCnNpZ19jaHJvbW9zb21lc19VTgpgYGAKCm5vdyBmb3IgZWFjaCBzaWduaWZpY2FudCBjaHJvbW9zb21lL3RyYWl0IGNvbWJvIHJ1biBiYXllc2ludAoKYGBge3J9CmJheWVzaW50X2xpc3RfVU4gPC0gYXBwbHkoc2lnX2Nocm9tb3NvbWVzX1VOLDEsZnVuY3Rpb24oaGl0KSB7CiAgcmVzdWx0IDwtIGJheWVzaW50KHNjYW5vbmVfQ0lNX1VOW2MoImNociIsInBvcyIsaGl0WyJnZW5lIl0pXSwgCiAgICAgICAgICAgICAgICAgICAgIGNocj1oaXRbImNociJdLCAKICAgICAgICAgICAgICAgICAgICAgbG9kY29sdW1uID0gMSwKICAgICAgICAgICAgICAgICAgICAgcHJvYj0wLjk5LAogICAgICAgICAgICAgICAgICAgICBleHBhbmR0b21hcmtlcnMgPSBUUlVFCiAgKQogIGNvbG5hbWVzKHJlc3VsdClbM10gPC0gIkxPRCIKICByZXN1bHQKfSkKbmFtZXMoYmF5ZXNpbnRfbGlzdF9VTikgPC0gc2lnX2Nocm9tb3NvbWVzX1VOJGdlbmUKCmJheWVzaW50X2xpc3RfVU4gPC0gbGFwcGx5KGJheWVzaW50X2xpc3RfVU4sZnVuY3Rpb24oeCkgeCAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuZGF0YS5mcmFtZShzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3duYW1lc190b19jb2x1bW4odmFyPSJtYXJrZXJuYW1lIikgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKGNocj1hcy5jaGFyYWN0ZXIoY2hyKSkKKQpgYGAKCmBgYHtyfQpiYXllc2ludF9yZXN1bHRfVU4gPC0gYXMudGliYmxlKGJpbmRfcm93cyhiYXllc2ludF9saXN0X1VOLC5pZD0iZ2VuZSIpKSAlPiUgCiAgc2VsZWN0KGdlbmUsY2hyLHBvcyxtYXJrZXJuYW1lLExPRCkgJT4lCiAgc2VwYXJhdGUobWFya2VybmFtZSxpbnRvPWMoImNocjEiLCJNYnAiKSxzZXA9IngiLCBjb252ZXJ0PVRSVUUpICU+JQogIGdyb3VwX2J5KGdlbmUsY2hyKSAlPiUKICBzdW1tYXJpemUoZVFUTF9zdGFydD1taW4oTWJwKSxlUVRMX2VuZD1tYXgoTWJwKSxtaW5fZVFUTF9MT0Q9bWluKExPRCksbWF4X2VRVExfTE9EPW1heChMT0QpKSAlPiUKICAjZm9yIHRoZSBoaWdoIFFUTCBwZWFrcyB0aGUgaW50ZXJ2YWwgd2lkdGggaXMgMC4gIFRoYXQgaXMgb3Zlcmx5IHByZWNpc2UgYW5kIG5lZWQgdG8gd2lkZW4gdGhvc2UuCiAgbXV0YXRlKGVRVExfc3RhcnQ9aWZlbHNlKGVRVExfc3RhcnQ9PWVRVExfZW5kLG1heCgwLGVRVExfc3RhcnQtMjAwMDApLGVRVExfc3RhcnQpLAogICAgICAgICBlUVRMX2VuZD1pZmVsc2UoZVFUTF9zdGFydD09ZVFUTF9lbmQsZVFUTF9lbmQrMjAwMDAsZVFUTF9lbmQpKQoKYmF5ZXNpbnRfcmVzdWx0X1VOCmBgYAoKIyMgYW5ub3RhdGUgVU4gZVFUTAoKTG9hZCBhbm5vdGF0aW9uCmBgYHtyfQpCcmFwYUFubm90YXRpb24gPC0gcmVhZF9jc3YoIi4uL2lucHV0L0JyYXBhX1YxLjVfYW5ub3RhdGVkLmNzdiIpCkJyYXBhQW5ub3RhdGlvbgpgYGAKCgpgYGB7cn0KVU5fYW5ub3RhdGVkIDwtIGxhcHBseSgxOm5yb3coYmF5ZXNpbnRfcmVzdWx0X1VOKSxmdW5jdGlvbihyb3cpIHsKICBxdGwgPC0gYmF5ZXNpbnRfcmVzdWx0X1VOW3JvdyxdCiAgc3Vic2V0KEJyYXBhQW5ub3RhdGlvbiwgY2hyb209PXF0bCRjaHIgJgogICAgICAgICAgIHN0YXJ0ID49IHF0bCRlUVRMX3N0YXJ0ICYKICAgICAgICAgICBlbmQgPD0gcXRsJGVRVExfZW5kKSAKfQopCm5hbWVzKFVOX2Fubm90YXRlZCkgPC0gYmF5ZXNpbnRfcmVzdWx0X1VOJGdlbmUKVU5fYW5ub3RhdGVkIDwtIGJpbmRfcm93cyhVTl9hbm5vdGF0ZWQsLmlkPSJNUl9nZW5lIikgJT4lCiAgbGVmdF9qb2luKGJheWVzaW50X3Jlc3VsdF9VTixieT1jKCJNUl9nZW5lIj0iZ2VuZSIsImNocm9tIj0iY2hyIikpICU+JSAjZ2V0IGVRVEwgTE9ECiAgbGVmdF9qb2luKE1SX1VOX2dlbmVzLGJ5PWMoIk1SX2dlbmUiPSJuYW1lIikpICU+JSAjIGdldCBjdXRvZmYKICBkcGx5cjo6cmVuYW1lKGVRVExfY2FuZGlkYXRlPW5hbWUpCgoKVU5fYW5ub3RhdGVkX3NtYWxsIDwtIFVOX2Fubm90YXRlZCAlPiUgc2VsZWN0KE1SX2dlbmUsTVJfQ3V0b2ZmLGVRVExfY2hyb209Y2hyb20sIGVRVExfc3RhcnQsIGVRVExfZW5kLCBlUVRMX2NhbmRpZGF0ZSxlbmRzX3dpdGgoIkxPRCIpKQoKVU5fYW5ub3RhdGVkX3NtYWxsCgp3cml0ZV9jc3YoVU5fYW5ub3RhdGVkX3NtYWxsLCBwYXRoPXN0cl9jKCIuLi9vdXRwdXQvIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZWJhc2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJfTVJfZVFUTF9VTl9BTExfQ0lNXyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN5cy5EYXRlKCksICIuY3N2IikpCmBgYAoKY2lzIGVRVEw/CmBgYHtyfQpVTl9hbm5vdGF0ZWRfc21hbGwgJT4lIGZpbHRlcihNUl9nZW5lPT1lUVRMX2NhbmRpZGF0ZSkKYGBgCgoKZ2l2ZW4gYmF5ZXNpbnQgcmVzdWx0cywgZmluZCBvdmVybGFwcyB3aXRoIFVOIGdyb3d0aCBRVEwKCmBgYHtyfQpVTl9NUmVRVExfUVRMX2NvbWJpbmVkIDwtIGlubmVyX2pvaW4oUVRMZ2VuZXMsVU5fYW5ub3RhdGVkX3NtYWxsLGJ5PWMoIm5hbWUiPSJlUVRMX2NhbmRpZGF0ZSIpKSAlPiUKICBzZWxlY3QoLmlkLCBNUl9nZW5lLCBNUl9DdXRvZmYsIGVRVExfc3RhcnQsIGVRVExfZW5kLCBlbmRzX3dpdGgoIkxPRCIpLCBldmVyeXRoaW5nKCkpICU+JQogIGFycmFuZ2UoLmlkLGRlc2MobWF4X2VRVExfTE9EKSkgJT4lCiAgZHBseXI6OnJlbmFtZShlUVRMX2NhbmRpZGF0ZT1uYW1lKQpVTl9NUmVRVExfUVRMX2NvbWJpbmVkCgpVTl9NUmVRVExfUVRMX2NvbWJpbmVkX3NtYWxsIDwtIFVOX01SZVFUTF9RVExfY29tYmluZWQgJT4lIGZpbHRlcighZHVwbGljYXRlZChlUVRMX2NhbmRpZGF0ZSkpICU+JQogIHNlbGVjdCgtTVJfZ2VuZSwtTVJfQ3V0b2ZmKSAKVU5fTVJlUVRMX1FUTF9jb21iaW5lZF9zbWFsbApgYGAKCmNpcyBlUVRMPwpgYGB7cn0KVU5fTVJlUVRMX1FUTF9jb21iaW5lZCAlPiUgZmlsdGVyKE1SX2dlbmU9PWVRVExfY2FuZGlkYXRlKSAlPiUgYXJyYW5nZShNUl9nZW5lKQpgYGAKClRvdGFsIG51bWJlciBvZiBNUiBnZW5lcyB3aXRoIGFuIGVRVEwgdGhhdCBvdmVybGFwcyB3aXRoIGFuIEZWVApgYGB7cn0KVU5fTVJlUVRMX1FUTF9jb21iaW5lZCAlPiUgc2VsZWN0KE1SX2dlbmUpICU+JSB1bmlxdWUoKQpgYGAKCgpob3cgbWFueSBRVEwgaGF2ZSBhdCBsZWFzdCBzb21lIG92ZXJsYXA/CmBgYHtyfQpsZW5ndGgodW5pcXVlKFFUTGdlbmVzJC5pZCkpCmxlbmd0aCh1bmlxdWUoVU5fTVJlUVRMX1FUTF9jb21iaW5lZCQuaWQpKQpgYGAKCjEwIG9mIDE2CgpgYGB7cn0Kd3JpdGVfY3N2KFVOX01SZVFUTF9RVExfY29tYmluZWQsCiAgICAgICAgICBwYXRoPXN0cl9jKCIuLi9vdXRwdXQvIiwgZmlsZWJhc2UsICJfTVJfZVFUTF9VTl9vdmVybGFwX0NJTV8iLCBTeXMuRGF0ZSgpLCAiLmNzdiIpKQpgYGAKCkhvdyB0byBhc3Nlc3MgaWYgb3ZlcmxhcCBpcyBzaWduaWZpY2FudD8KCkkgdGhpbmsgcHVsbCByZWdpb25zIG9mIHNhbWUgc2l6ZSBhcyBlUVRMIGFuZCBhc2sgaG93IG9mdGVuIHRoZXkgb3ZlcmxhcCB3aXRoIGdyb3d0aCBRVEwuCgpGb3IgZWFjaCBlUVRMLCByYW5kb21seSBzZWxlY3QgYSBjaHJvbW9zb21lLCB0aGVuIGEgcG9zaXRpb24sIGFuZCB3aWRlbiBiYXNlZCBvbiBpbnRlcnZhbC4gIFRoZW4gY2hlY2sgb3ZlcmxhcC4gIFJlcGVhdC4KCk1ha2UgdGFibGUgb2YgY2hyb21vc29tZSBpbmZvCmBgYHtyfQpjaHIuaW5mbyA8LSBzY2Fub25lX0NJTV9VTiAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigibWFya2VyIikgJT4lCiAgc2VsZWN0KG1hcmtlcikgJT4lCiAgc2VwYXJhdGUobWFya2VyLGludG89YygiY2hyIiwiYnAiKSxzZXA9IngiLGNvbnZlcnQ9VFJVRSkgJT4lCiAgZ3JvdXBfYnkoY2hyKSAlPiUKICBzdW1tYXJpemUoc3RhcnQ9bWluKGJwKSxlbmQ9bWF4KGJwKSkKYGBgCgpNYWtlIGEgdGFibGUgb2YgUVRMIGluZm8KCmBgYHtyfQpxdGwuaW5mbyA8LSBRVExnZW5lcyAlPiUKICBncm91cF9ieSguaWQpICU+JQogIHN1bW1hcml6ZShjaHJvbT11bmlxdWUoY2hyb20pLHN0YXJ0PW1pbihzdGFydCksZW5kPW1heChlbmQpKQpxdGwuaW5mbwpxdGwucmFuZ2VzIDwtIEdSYW5nZXMoc2VxbmFtZXMgPSBxdGwuaW5mbyRjaHJvbSxyYW5nZXM9SVJhbmdlcyhzdGFydD1xdGwuaW5mbyRzdGFydCxlbmQ9cXRsLmluZm8kZW5kKSkKcXRsLnJhbmdlcyA8LSBHZW5vbWljUmFuZ2VzOjpyZWR1Y2UocXRsLnJhbmdlcykKYGBgCgoKVGhlIGVRVEwgYXJlIGluIEJheWVzaW50X3Jlc3VsdHMKCgpgYGB7cn0Kc2ltcyA8LSAxMDAwCmVRVEwucmFuZ2VzIDwtIEdSYW5nZXMoYmF5ZXNpbnRfcmVzdWx0X1VOJGNociwKICAgICAgICAgICAgICAgICAgICAgICByYW5nZXMgPSBJUmFuZ2VzKHN0YXJ0PWJheWVzaW50X3Jlc3VsdF9VTiRlUVRMX3N0YXJ0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kPWJheWVzaW50X3Jlc3VsdF9VTiRlUVRMX2VuZCkpCmVRVEwucmFuZ2VzIDwtIEdlbm9taWNSYW5nZXM6OnJlZHVjZShlUVRMLnJhbmdlcykKCnNldC5zZWVkKDU0MzIxKQpzaW0ucmVzdWx0cyA8LSBzYXBwbHkoMTpzaW1zLCBmdW5jdGlvbihzKSB7CiAgc2ltLmVRVEwgPC0gdGliYmxlKAogICAgY2hyPXNhbXBsZShjaHIuaW5mbyRjaHIsCiAgICAgICAgICAgICAgIHNpemUgPSBsZW5ndGgoZVFUTC5yYW5nZXMpLAogICAgICAgICAgICAgICByZXBsYWNlID0gVFJVRSwKICAgICAgICAgICAgICAgcHJvYj1jaHIuaW5mbyRlbmQvc3VtKGNoci5pbmZvJGVuZCkpLAogICAgd2lkdGg9d2lkdGgoZVFUTC5yYW5nZXMpICMgd2lkdGggb2YgdGhlIFFUTCB0byBzaW11bGF0ZQogICkKICBzaW0uZVFUTCA8LSBjaHIuaW5mbyAlPiUgCiAgICBzZWxlY3QoY2hyLGNoci5zdGFydD1zdGFydCxjaHIuZW5kPWVuZCkgJT4lIHJpZ2h0X2pvaW4oc2ltLmVRVEwsYnk9ImNociIpICNuZWVkIHRvIGdldCB0aGUgY2hyb20gZW5kIHNvIHdlIGNhbiBzYW1wbGUgY29ycmVjdGx5CiAgc2ltLmVRVEwgPC0gc2ltLmVRVEwgJT4lIG11dGF0ZShxdGwuc3RhcnQgPSBydW5pZihuPW4oKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbiA9IGNoci5zdGFydCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heD0gbWF4KGNoci5zdGFydCxjaHIuZW5kLXdpZHRoKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdGwuZW5kPXF0bC5zdGFydCt3aWR0aCkKICBzaW0uZVFUTC5yYW5nZXMgPC0gR1JhbmdlcyhzZXFuYW1lcyA9IHNpbS5lUVRMJGNocixyYW5nZXMgPSBJUmFuZ2VzKHN0YXJ0PXNpbS5lUVRMJHF0bC5zdGFydCxlbmQ9c2ltLmVRVEwkcXRsLmVuZCkpCiAgc3VwcHJlc3NXYXJuaW5ncyhyZXN1bHQgPC0gc3VtKGNvdW50T3ZlcmxhcHMocXRsLnJhbmdlcyxzaW0uZVFUTC5yYW5nZXMpPjApKQogIHJlc3VsdAp9KQoKYGBgCgoKYGBge3J9CnRydWUub3ZlcmxhcCA8LSBzdW0oY291bnRPdmVybGFwcyhxdGwucmFuZ2VzLGVRVEwucmFuZ2VzKSkgI09LIHRvIGlnbm9yZSB3YXJuaW5ncwoKdHJ1ZS5vdmVybGFwCgptZWFuKHNpbS5yZXN1bHRzID49IHRydWUub3ZlcmxhcCkKCnRpYmJsZShGVlRRVExfdnNfTVJlUVRMX1RydWVfT3ZlcmxhcHM9dHJ1ZS5vdmVybGFwLAogICAgICAgTl9TaW11bGF0aW9uc19mZXdlcl9vdmVybGFwcz1zdW0oc2ltLnJlc3VsdHMgPCB0cnVlLm92ZXJsYXApLAogICAgICAgTl9TaW11bGF0aW9uc19ncmVhdGVyX2VxdWFsX292ZXJsYXBzPXN1bShzaW0ucmVzdWx0cyA+PSB0cnVlLm92ZXJsYXApLAogICAgICAgUF92YWx1ZT1tZWFuKHNpbS5yZXN1bHRzID49IHRydWUub3ZlcmxhcCkKKSAlPiUKICB3cml0ZV9jc3Yoc3RyX2MoIi4uL291dHB1dC8iLCBmaWxlYmFzZSwgIl9NUmVRVExfb3ZlcmxhcF9wdmFsX0NJTSIsIFN5cy5EYXRlKCksICIuY3N2IikpCmBgYAoKc2lnbmlmaWNhbnQgYXQgcCA9IDAuMDAzOyBvbmx5IDAuMyUgb2YgdGhlIHNpbXVsYXRpb25zIGhhZCBhcyBtYW55IG92ZXJsYXBzIGFzIGluIHRoZSB0cnVlIGRhdGEgc2V0Lgo=